using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using BSDS.Core.Data.TemporaryFiles;
using BSDS.Core.Model;
using BSDS.Core.Model.Data;

namespace BSDS.Core.Data.Remote.Geocoding.Serialization
{
    public class GeoSerializer
    {
        private readonly SerializationUtils utils = new SerializationUtils();
        private readonly FilenameProvider filenameProvider;

        public GeoSerializer(FilenameProvider filenameProvider)
        {
            this.filenameProvider = filenameProvider;
        }

        public string SerializeToGeoRequest(EntityValue[] entities)
        {
            var filename = filenameProvider.New("geocoding");

            Serialize(filename, entities, GeoRequestSerializer);

            return filename;
        }

        public string SerializeToReverseGeoRequest(EntityValue[] entities)
        {
            var filename = filenameProvider.New("reversegeo");

            Serialize(filename, entities, ReverseGeoRequestSerializer);

            return filename;
        }

        private static void Serialize(string filename, EntityValue[] values, Action<EntityValue[], StreamWriter> serializer)
        {
            using (var xmlWriter = new StreamWriter(new FileStream(filename, FileMode.OpenOrCreate)))
            {
                serializer.Invoke(values, xmlWriter);
            }
        }

        private void GeoRequestSerializer(EntityValue[] entities, StreamWriter xmlWriter)
        {
            xmlWriter.Write(GeoRequestTemplates.Header);

            GenerateGeocodingFeeds(entities, xmlWriter);

            xmlWriter.Write(GeoRequestTemplates.Trailer);
        }

        private void GenerateGeocodingFeeds(IEnumerable<EntityValue> entities, StreamWriter xmlWriter)
        {
            foreach (var entity in entities)
            {
                var entityId = entity.PrimaryKeyValue;
                var addressLine = utils.EscapeSpecialCharacters(entity.GetAttributeValue("AddressLine"));
                var adminDistrict = utils.EscapeSpecialCharacters(entity.GetAttributeValue("AdminDistrict"));
                var locality = utils.EscapeSpecialCharacters(entity.GetAttributeValue("Locality"));
                var countryRegion = utils.EscapeSpecialCharacters(entity.GetAttributeValue("CountryRegion"));
                var postalCode = utils.EscapeSpecialCharacters(entity.GetAttributeValue("PostalCode"));

                var feedTemplate = GeoRequestTemplates.GeoRequestEntry;
                var singleFeed = string.Format(feedTemplate, entityId, addressLine, adminDistrict, locality,
                                               countryRegion, postalCode);

                xmlWriter.Write(singleFeed);
            }
        }

        private void ReverseGeoRequestSerializer(EntityValue[] entities, StreamWriter xmlWriter)
        {
            xmlWriter.Write(GeoRequestTemplates.Header);

            GenerateReverseGeocodingFeeds(entities, xmlWriter);

            xmlWriter.Write(GeoRequestTemplates.Trailer);
        }

        private void GenerateReverseGeocodingFeeds(IEnumerable<EntityValue> entities, StreamWriter xmlWriter)
        {
            foreach (var entity in entities)
            {
                var entityId = entity.AttributeValues.First(av => av.AttributeInfo.IsPrimaryKey).Value;
                var longitude = utils.EscapeSpecialCharacters(entity.GetAttributeValue("Longitude"));
                var latitude = utils.EscapeSpecialCharacters(entity.GetAttributeValue("Latitude"));

                var feedTemplate = GeoRequestTemplates.ReverseGeoRequestEntry;
                var singleFeed = string.Format(feedTemplate, entityId, longitude, latitude);

                xmlWriter.Write(singleFeed);
            }
        }
    }
}